Skip to content

PPSC-1037: Add auth login/logout/whoami with OAuth2 device flow#257

Open
dmitrysmirnov-armis wants to merge 5 commits into
mainfrom
feat/PPSC-1037-armis-cli-add-auth-loginlogoutwhoami-commands-with-device-au
Open

PPSC-1037: Add auth login/logout/whoami with OAuth2 device flow#257
dmitrysmirnov-armis wants to merge 5 commits into
mainfrom
feat/PPSC-1037-armis-cli-add-auth-loginlogoutwhoami-commands-with-device-au

Conversation

@dmitrysmirnov-armis

@dmitrysmirnov-armis dmitrysmirnov-armis commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

Related Issue

Type of Change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Documentation update
  • Refactoring (no functional changes)
  • Performance improvement

Problem

The CLI only supported static credentials (client-credentials JWT and a legacy API token). Developers had no way to authenticate via browser-based SSO against their corporate IdP, and there was no shared session that the CLI and the Armis MCP plugins could both reuse from a single sign-in.

Solution

Implements the auth command group built on the OAuth2 Device Authorization Grant (RFC 8628):

  • auth login — requests a device code (POST /oauth2/device, client_id defaults to armis-cli), auto-opens the browser at verification_uri_complete (falls back to printing the URL + user_code over SSH/headless), then polls POST /oauth2/token honoring authorization_pending / slow_down / expired_token / access_denied. On success it stores the token and prints the identity and tenant.
  • auth logout — clears the stored token for the current environment; --all clears every environment.
  • auth whoami — shows environment, auth method, identity, tenant, region, and token expiry.
  • Auth resolution order — stored SSO token → ARMIS_CLIENT_ID/ARMIS_CLIENT_SECRET → legacy ARMIS_API_TOKEN → an error pointing at auth login. Explicit --client-id/--client-secret/--token override the stored token (escape hatch). CI/CD env-var auth is unchanged.
  • Auto-refresh — transparently refreshes the access token via the refresh-token grant near expiry and persists the rotated pair.

Token storage is a 0600 JSON file at ~/.armis/.sessions (a JSON array of {env, token} keyed by the resolved API base URL), giving a single source of truth that the Python MCP plugins can share — go-keyring does not interoperate with Python's keyring, and the backend's refresh-token reuse-detection makes a divergent second store dangerous.

Changes layered on top of the original ticket scope:

  • Commands are now visible (auth login/logout/whoami). The original auth (raw JWT from client credentials) is preserved as a hidden auth token subcommand.
  • New ARMIS_DEFAULT_AUTH_METHOD env var — set to SSO to auto-trigger the device-flow login on the first command that needs credentials when none are configured (requires ARMIS_TENANT_ID/--tenant-id). Gated so it never shadows a working CI/service-account setup.
  • README — restructured the Environment Variables section to recommend client credentials for CI/CD and SSO for interactive use, and documented ARMIS_DEFAULT_AUTH_METHOD.

Testing

Automated Tests

  • Unit tests added/updated
  • Integration tests added/updated
  • All tests passing locally

New/updated coverage: device-flow client (device_test.go), token store (tokenstore_test.go), OAuth provider refresh (oauth_provider_test.go), browser opener (browser_test.go), and the end-to-end command flow including the SSO auto-login gating and trigger (auth_flow_test.go).

Manual Testing

go build ./..., go test ./internal/cmd/ ./internal/auth/, and golangci-lint run all pass locally. End-to-end login/whoami/logout exercised against a mock OAuth2 device-flow server in auth_flow_test.go.

Reviewer Notes

  • getAuthProvider now takes a context.Context so the interactive auto-login can run for the command's full lifetime rather than a short request timeout.

Checklist

  • Code follows project style guidelines
  • Pre-commit hooks pass
  • Self-review performed
  • Documentation updated (if needed)
  • Breaking changes documented (if applicable)
  • No new warnings generated

@github-actions

Copy link
Copy Markdown

Armis AppSecArmis AppSec Security Scan Results

🟠 HIGH issues found

Severity Count
🟠 HIGH 1
🟡 MEDIUM 1

Total: 2

View all 2 findings

🟠 HIGH (1)

CWE-327 - Cryptography Failures (CWE-327

Location: internal/auth/device.go:258

Use of Broken or Risky Cryptographic Algorithm): The program talks to the authentication server and receives a JWT access token. It then calls parseAccessTokenClaims to read the token’s contents, but it never checks the token’s digital signature. Because the signature isn’t verified, anyone who can supply a forged token could make the client believe the token contains whatever user, role, or tenant information they want. The client would then use those false claims for things like deciding which tenant it belongs to or what permissions it has. Since the token comes from a network request, an attacker who can influence that response could exploit this weakness. Adding proper signature verification (or using a library that does it automatically) would prevent the client from trusting tampered tokens.

Code snippet is redacted as it contains secrets.

CWEs: CWE-327: Use of Broken or Risky Cryptographic Algorithm

🟡 MEDIUM (1)

CWE-253 - In the `runAuthLogout` function the code calls `store.Load(env)` and assigns the result to `existing`, but it completely ignores the second return value (the error)

Location: internal/cmd/auth_logout.go:53

In the runAuthLogout function the code calls store.Load(env) and assigns the result to existing, but it completely ignores the second return value (the error). If store.Load fails—for example because the token file is missing, corrupted, or the program lacks permission—the error is discarded and the program proceeds as if nothing was stored. This means a problem with loading the credentials can go unnoticed, and the user will see a misleading message that no credentials were removed. Because this command is part of a command‑line tool that any user can run, the mistake can be triggered simply by invoking the logout command. There is no additional check or fallback to handle a load failure, so the vulnerable code may hide real errors and give a false sense of security. To fix the issue, the code should examine the error returned by store.Load and handle it appropriately (e.g., report the failure to the user or abort the operation). This ensures that problems with reading stored credentials are not silently ignored.

			return nil
		}
		fmt.Fprintf(os.Stderr, "%s Signed out of %d environment(s).\n", output.IconSuccess, len(envs))
		return nil
	}

	env := getAPIBaseURL()

	// Report whether anything was actually stored, so logout is informative
	// rather than silently succeeding when not logged in.
	existing, _ := store.Load(env)
	if err := store.Clear(env); err != nil {
		return fmt.Errorf("failed to remove stored credentials: %w", err)
	}

	if existing == nil {
		fmt.Fprintf(os.Stderr, "No stored credentials to remove for %s.\n", env)
		return nil
	}
	fmt.Fprintf(os.Stderr, "%s Signed out of %s.\n", output.IconSuccess, env)
	return nil

CWEs: CWE-253: Incorrect Check of Function Return Value

Comment thread internal/auth/device.go Dismissed
Comment thread internal/cmd/auth_logout.go Dismissed
@github-actions

Copy link
Copy Markdown

Test Coverage Report

total: (statements) 73.8%

Coverage by function
github.com/ArmisSecurity/armis-cli/cmd/armis-cli/main.go:19:				main					0.0%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/agent.go:44:			Registry				100.0%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/agent.go:86:			RegisteredAgentDisplayNames		85.7%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/agentdetect.go:29:		FlatResults				100.0%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/agentdetect.go:45:		NewScanner				100.0%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/agentdetect.go:53:		Scan					82.4%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/detector.go:12:			resolvePath				100.0%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/detector.go:23:			isUnderDir				81.8%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/detector.go:46:			dirExists				100.0%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/detector.go:56:			fileExists				100.0%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/detector.go:67:			hasExtensionPrefix			80.0%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/detector.go:88:			findExtensionVersion			64.3%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/detector.go:111:		readVersionFromPackageJSON		71.4%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/detector.go:127:		hasJetBrainsPlugin			100.0%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/detector.go:140:		Name					100.0%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/detector.go:142:		Detect					100.0%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/detector.go:147:		CheckMCP				100.0%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/detector.go:151:		DetectVersion				100.0%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/detector.go:159:		Name					100.0%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/detector.go:161:		Detect					100.0%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/detector.go:171:		CheckMCP				100.0%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/detector.go:175:		DetectVersion				100.0%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/detector.go:183:		Name					100.0%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/detector.go:185:		Detect					100.0%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/detector.go:189:		CheckMCP				100.0%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/detector.go:193:		DetectVersion				0.0%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/detector.go:201:		Name					100.0%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/detector.go:203:		Detect					100.0%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/detector.go:216:		CheckMCP				100.0%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/detector.go:224:		DetectVersion				0.0%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/detector.go:232:		Name					100.0%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/detector.go:234:		Detect					100.0%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/detector.go:241:		CheckMCP				100.0%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/detector.go:245:		DetectVersion				100.0%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/detector.go:253:		Name					100.0%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/detector.go:255:		Detect					100.0%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/detector.go:262:		CheckMCP				100.0%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/detector.go:268:		DetectVersion				100.0%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/detector.go:276:		Name					100.0%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/detector.go:278:		Detect					100.0%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/detector.go:285:		CheckMCP				100.0%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/detector.go:289:		DetectVersion				100.0%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/detector.go:297:		Name					100.0%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/detector.go:299:		Detect					100.0%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/detector.go:306:		CheckMCP				0.0%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/detector.go:310:		DetectVersion				0.0%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/detector.go:318:		Name					100.0%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/detector.go:320:		Detect					100.0%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/detector.go:324:		CheckMCP				0.0%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/detector.go:328:		DetectVersion				0.0%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/detector.go:336:		Name					100.0%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/detector.go:338:		Detect					100.0%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/detector.go:342:		CheckMCP				0.0%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/detector.go:346:		DetectVersion				0.0%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/detector.go:354:		Name					100.0%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/detector.go:356:		Detect					100.0%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/detector.go:366:		CheckMCP				100.0%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/detector.go:370:		DetectVersion				100.0%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/detector.go:378:		Name					100.0%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/detector.go:380:		Detect					100.0%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/detector.go:395:		CheckMCP				100.0%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/detector.go:399:		DetectVersion				0.0%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/detector.go:407:		Name					100.0%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/detector.go:409:		Detect					100.0%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/detector.go:417:		CheckMCP				75.0%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/detector.go:425:		DetectVersion				0.0%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/detector.go:433:		Name					100.0%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/detector.go:435:		Detect					100.0%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/detector.go:439:		CheckMCP				83.3%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/detector.go:460:		DetectVersion				0.0%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/detector.go:468:		Name					100.0%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/detector.go:470:		Detect					100.0%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/detector.go:474:		CheckMCP				100.0%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/detector.go:478:		DetectVersion				0.0%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/format.go:13:			FormatPlain				78.6%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/format.go:62:			anyMCPMissing				100.0%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/format.go:74:			FormatJSON				100.0%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/mcpconfig.go:19:		HasArmisMCP				83.3%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/mcpconfig.go:40:		HasArmisMCPInClaudeSettings		86.7%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/mcpconfig.go:68:		HasArmisMCPInZedSettings		66.7%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/mcpconfig.go:98:		HasArmisMCPInVSCodeFormat		75.0%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/mcpconfig.go:122:		hasArmisMCPInData			100.0%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/platform_linux.go:13:		NewPlatform				0.0%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/platform_linux.go:17:		UserHomeDirs				0.0%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/platform_linux.go:25:		VSCodeExtensionsDir			0.0%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/platform_linux.go:30:		JetBrainsPluginDirs			0.0%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/platform_linux.go:35:		VSCodeUserConfigDir			0.0%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/platform_linux.go:39:		CursorAppExists				0.0%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/platform_linux.go:43:		JunieBinaryPaths			0.0%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/platform_linux.go:51:		ZedConfigDir				0.0%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/platform_linux.go:55:		IsRoot					0.0%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/userprofile.go:13:		enumerateUserDirs			0.0%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/userprofile.go:41:		currentUserOnly				0.0%
github.com/ArmisSecurity/armis-cli/internal/agentdetect/userprofile.go:56:		globJetBrainsPluginDirs			0.0%
github.com/ArmisSecurity/armis-cli/internal/api/agents.go:30:				ReportAgentInventory			78.9%
github.com/ArmisSecurity/armis-cli/internal/api/client.go:31:				Error					0.0%
github.com/ArmisSecurity/armis-cli/internal/api/client.go:62:				isLoopbackHost				100.0%
github.com/ArmisSecurity/armis-cli/internal/api/client.go:86:				copyWithContext				70.4%
github.com/ArmisSecurity/armis-cli/internal/api/client.go:159:				WithHTTPClient				100.0%
github.com/ArmisSecurity/armis-cli/internal/api/client.go:168:				WithUploadHTTPClient			100.0%
github.com/ArmisSecurity/armis-cli/internal/api/client.go:176:				WithAllowLocalURLs			100.0%
github.com/ArmisSecurity/armis-cli/internal/api/client.go:188:				NewClient				100.0%
github.com/ArmisSecurity/armis-cli/internal/api/client.go:236:				IsDebug					100.0%
github.com/ArmisSecurity/armis-cli/internal/api/client.go:250:				setAuthHeader				77.8%
github.com/ArmisSecurity/armis-cli/internal/api/client.go:295:				StartIngest				100.0%
github.com/ArmisSecurity/armis-cli/internal/api/client.go:344:				createPresignedUpload			80.8%
github.com/ArmisSecurity/armis-cli/internal/api/client.go:406:				uploadToPresignedURL			89.5%
github.com/ArmisSecurity/armis-cli/internal/api/client.go:457:				buildMultipartEnvelope			80.0%
github.com/ArmisSecurity/armis-cli/internal/api/client.go:489:				startArtifactScan			75.0%
github.com/ArmisSecurity/armis-cli/internal/api/client.go:541:				GetIngestStatus				82.6%
github.com/ArmisSecurity/armis-cli/internal/api/client.go:582:				WaitForIngest				84.6%
github.com/ArmisSecurity/armis-cli/internal/api/client.go:633:				FetchNormalizedResults			74.2%
github.com/ArmisSecurity/armis-cli/internal/api/client.go:688:				FetchAllNormalizedResults		91.7%
github.com/ArmisSecurity/armis-cli/internal/api/client.go:714:				GetScanResult				68.4%
github.com/ArmisSecurity/armis-cli/internal/api/client.go:749:				WaitForScan				90.0%
github.com/ArmisSecurity/armis-cli/internal/api/client.go:770:				formatBytes				100.0%
github.com/ArmisSecurity/armis-cli/internal/api/client.go:792:				FetchArtifactScanResults		75.0%
github.com/ArmisSecurity/armis-cli/internal/api/client.go:847:				ValidatePresignedURL			94.1%
github.com/ArmisSecurity/armis-cli/internal/api/client.go:902:				DownloadFromPresignedURL		84.2%
github.com/ArmisSecurity/armis-cli/internal/auth/auth.go:86:				NewProviderFromStored			60.0%
github.com/ArmisSecurity/armis-cli/internal/auth/auth.go:103:				AuthMethod				50.0%
github.com/ArmisSecurity/armis-cli/internal/auth/auth.go:116:				Identity				0.0%
github.com/ArmisSecurity/armis-cli/internal/auth/auth.go:127:				Expiry					0.0%
github.com/ArmisSecurity/armis-cli/internal/auth/auth.go:142:				NewAuthProvider				95.2%
github.com/ArmisSecurity/armis-cli/internal/auth/auth.go:189:				GetAuthorizationHeader			92.3%
github.com/ArmisSecurity/armis-cli/internal/auth/auth.go:218:				GetTenantID				69.2%
github.com/ArmisSecurity/armis-cli/internal/auth/auth.go:244:				GetRegion				53.8%
github.com/ArmisSecurity/armis-cli/internal/auth/auth.go:268:				IsLegacy				100.0%
github.com/ArmisSecurity/armis-cli/internal/auth/auth.go:281:				GetRawToken				53.8%
github.com/ArmisSecurity/armis-cli/internal/auth/auth.go:319:				exchangeCredentials			89.7%
github.com/ArmisSecurity/armis-cli/internal/auth/auth.go:412:				refreshIfNeeded				100.0%
github.com/ArmisSecurity/armis-cli/internal/auth/auth.go:428:				refreshOAuthIfNeeded			71.9%
github.com/ArmisSecurity/armis-cli/internal/auth/auth.go:503:				parseJWTClaims				93.3%
github.com/ArmisSecurity/armis-cli/internal/auth/browser.go:19:				SetBrowserOpener			100.0%
github.com/ArmisSecurity/armis-cli/internal/auth/browser.go:31:				OpenBrowser				83.3%
github.com/ArmisSecurity/armis-cli/internal/auth/browser.go:45:				openBrowserCmd				0.0%
github.com/ArmisSecurity/armis-cli/internal/auth/client.go:50:				RegionalBaseURL				100.0%
github.com/ArmisSecurity/armis-cli/internal/auth/client.go:68:				Error					100.0%
github.com/ArmisSecurity/armis-cli/internal/auth/client.go:80:				NewAuthClient				100.0%
github.com/ArmisSecurity/armis-cli/internal/auth/client.go:142:				Authenticate				75.8%
github.com/ArmisSecurity/armis-cli/internal/auth/client.go:232:				annotateTransportError			100.0%
github.com/ArmisSecurity/armis-cli/internal/auth/device.go:75:				Error					0.0%
github.com/ArmisSecurity/armis-cli/internal/auth/device.go:101:				NewDeviceClient				72.7%
github.com/ArmisSecurity/armis-cli/internal/auth/device.go:138:				RequestDeviceCode			72.2%
github.com/ArmisSecurity/armis-cli/internal/auth/device.go:171:				PollToken				60.9%
github.com/ArmisSecurity/armis-cli/internal/auth/device.go:218:				exchangeDeviceCode			100.0%
github.com/ArmisSecurity/armis-cli/internal/auth/device.go:229:				Refresh					100.0%
github.com/ArmisSecurity/armis-cli/internal/auth/device.go:241:				tokenRequest				76.5%
github.com/ArmisSecurity/armis-cli/internal/auth/device.go:283:				postForm				78.6%
github.com/ArmisSecurity/armis-cli/internal/auth/device.go:308:				parseError				75.0%
github.com/ArmisSecurity/armis-cli/internal/auth/device.go:317:				asOAuthError				40.0%
github.com/ArmisSecurity/armis-cli/internal/auth/device.go:351:				parseAccessTokenClaims			76.9%
github.com/ArmisSecurity/armis-cli/internal/auth/region_cache.go:34:			NewRegionCache				100.0%
github.com/ArmisSecurity/armis-cli/internal/auth/region_cache.go:40:			Load					82.4%
github.com/ArmisSecurity/armis-cli/internal/auth/region_cache.go:75:			Save					76.9%
github.com/ArmisSecurity/armis-cli/internal/auth/region_cache.go:105:			Clear					75.0%
github.com/ArmisSecurity/armis-cli/internal/auth/region_cache.go:115:			getFilePath				83.3%
github.com/ArmisSecurity/armis-cli/internal/auth/region_cache.go:132:			loadCachedRegion			100.0%
github.com/ArmisSecurity/armis-cli/internal/auth/region_cache.go:136:			saveCachedRegion			100.0%
github.com/ArmisSecurity/armis-cli/internal/auth/region_cache.go:140:			clearCachedRegion			100.0%
github.com/ArmisSecurity/armis-cli/internal/auth/tokenstore.go:88:			NewTokenStore				0.0%
github.com/ArmisSecurity/armis-cli/internal/auth/tokenstore.go:94:			normalizeEnv				100.0%
github.com/ArmisSecurity/armis-cli/internal/auth/tokenstore.go:99:			Save					83.3%
github.com/ArmisSecurity/armis-cli/internal/auth/tokenstore.go:131:			Load					90.9%
github.com/ArmisSecurity/armis-cli/internal/auth/tokenstore.go:151:			Clear					90.9%
github.com/ArmisSecurity/armis-cli/internal/auth/tokenstore.go:170:			ClearAll				0.0%
github.com/ArmisSecurity/armis-cli/internal/auth/tokenstore.go:175:			Environments				85.7%
github.com/ArmisSecurity/armis-cli/internal/auth/tokenstore.go:188:			Path					0.0%
github.com/ArmisSecurity/armis-cli/internal/auth/tokenstore.go:196:			read					73.7%
github.com/ArmisSecurity/armis-cli/internal/auth/tokenstore.go:227:			write					63.6%
github.com/ArmisSecurity/armis-cli/internal/auth/tokenstore.go:245:			remove					66.7%
github.com/ArmisSecurity/armis-cli/internal/auth/tokenstore.go:258:			filePath				42.9%
github.com/ArmisSecurity/armis-cli/internal/cli/color.go:60:				InitColors				85.2%
github.com/ArmisSecurity/armis-cli/internal/cli/color.go:107:				ColorsEnabled				100.0%
github.com/ArmisSecurity/armis-cli/internal/cli/color.go:113:				ColorsForced				100.0%
github.com/ArmisSecurity/armis-cli/internal/cli/color.go:119:				SetOutputToFile				100.0%
github.com/ArmisSecurity/armis-cli/internal/cli/color.go:125:				GetOutputToFile				0.0%
github.com/ArmisSecurity/armis-cli/internal/cli/color.go:129:				enableColors				100.0%
github.com/ArmisSecurity/armis-cli/internal/cli/color.go:136:				disableColors				100.0%
github.com/ArmisSecurity/armis-cli/internal/cli/color.go:151:				parseErrorMessage			92.9%
github.com/ArmisSecurity/armis-cli/internal/cli/color.go:182:				PrintError				100.0%
github.com/ArmisSecurity/armis-cli/internal/cli/color.go:195:				PrintErrorf				0.0%
github.com/ArmisSecurity/armis-cli/internal/cli/color.go:202:				PrintWarning				100.0%
github.com/ArmisSecurity/armis-cli/internal/cli/color.go:208:				PrintWarningf				100.0%
github.com/ArmisSecurity/armis-cli/internal/cli/interactive.go:11:			IsInteractive				0.0%
github.com/ArmisSecurity/armis-cli/internal/cmd/agent_detection.go:20:			agentDetectionLong			100.0%
github.com/ArmisSecurity/armis-cli/internal/cmd/agent_detection.go:47:			init					100.0%
github.com/ArmisSecurity/armis-cli/internal/cmd/agent_detection.go:53:			runAgentDetection			0.0%
github.com/ArmisSecurity/armis-cli/internal/cmd/agent_detection_collect.go:30:		init					100.0%
github.com/ArmisSecurity/armis-cli/internal/cmd/agent_detection_collect.go:34:		runAgentDetectionCollect		0.0%
github.com/ArmisSecurity/armis-cli/internal/cmd/agent_detection_collect.go:98:		buildInventoryPayload			100.0%
github.com/ArmisSecurity/armis-cli/internal/cmd/auth.go:50:				init					100.0%
github.com/ArmisSecurity/armis-cli/internal/cmd/auth.go:58:				runAuth					90.0%
github.com/ArmisSecurity/armis-cli/internal/cmd/auth_login.go:38:			init					100.0%
github.com/ArmisSecurity/armis-cli/internal/cmd/auth_login.go:43:			runAuthLogin				100.0%
github.com/ArmisSecurity/armis-cli/internal/cmd/auth_login.go:56:			performDeviceLogin			85.7%
github.com/ArmisSecurity/armis-cli/internal/cmd/auth_login.go:108:			printVerificationInstructions		62.5%
github.com/ArmisSecurity/armis-cli/internal/cmd/auth_login.go:121:			printLoginSuccess			100.0%
github.com/ArmisSecurity/armis-cli/internal/cmd/auth_logout.go:28:			init					100.0%
github.com/ArmisSecurity/armis-cli/internal/cmd/auth_logout.go:33:			runAuthLogout				78.9%
github.com/ArmisSecurity/armis-cli/internal/cmd/auth_whoami.go:23:			init					100.0%
github.com/ArmisSecurity/armis-cli/internal/cmd/auth_whoami.go:27:			runAuthWhoami				84.2%
github.com/ArmisSecurity/armis-cli/internal/cmd/auth_whoami.go:63:			describeAuthMethod			40.0%
github.com/ArmisSecurity/armis-cli/internal/cmd/auth_whoami.go:77:			humanizeUntil				42.9%
github.com/ArmisSecurity/armis-cli/internal/cmd/cmdutil/failon.go:15:			ValidateFailOn				100.0%
github.com/ArmisSecurity/armis-cli/internal/cmd/cmdutil/failon.go:37:			GetFailOn				100.0%
github.com/ArmisSecurity/armis-cli/internal/cmd/cmdutil/output.go:32:			Cleanup					100.0%
github.com/ArmisSecurity/armis-cli/internal/cmd/cmdutil/output.go:58:			ResolveOutput				96.4%
github.com/ArmisSecurity/armis-cli/internal/cmd/cmdutil/theme.go:27:			armisTheme				0.0%
github.com/ArmisSecurity/armis-cli/internal/cmd/cmdutil/theme.go:67:			GetInstallTheme				0.0%
github.com/ArmisSecurity/armis-cli/internal/cmd/context.go:24:				NewSignalContext			100.0%
github.com/ArmisSecurity/armis-cli/internal/cmd/context.go:33:				handleScanError				100.0%
github.com/ArmisSecurity/armis-cli/internal/cmd/help.go:30:				SetupHelp				91.7%
github.com/ArmisSecurity/armis-cli/internal/cmd/help.go:59:				styledUsageTemplate			100.0%
github.com/ArmisSecurity/armis-cli/internal/cmd/help.go:102:				defaultUsageTemplate			100.0%
github.com/ArmisSecurity/armis-cli/internal/cmd/help.go:109:				initColorsForHelp			35.3%
github.com/ArmisSecurity/armis-cli/internal/cmd/help.go:150:				styleHelpOutput				83.3%
github.com/ArmisSecurity/armis-cli/internal/cmd/hook.go:24:				init					100.0%
github.com/ArmisSecurity/armis-cli/internal/cmd/hook_init.go:32:			init					100.0%
github.com/ArmisSecurity/armis-cli/internal/cmd/hook_init.go:38:			runHookInit				66.7%
github.com/ArmisSecurity/armis-cli/internal/cmd/install.go:64:				init					100.0%
github.com/ArmisSecurity/armis-cli/internal/cmd/install.go:73:				runInstall				0.0%
github.com/ArmisSecurity/armis-cli/internal/cmd/install.go:109:				showInstalledVersions			84.6%
github.com/ArmisSecurity/armis-cli/internal/cmd/install.go:132:				installAll				0.0%
github.com/ArmisSecurity/armis-cli/internal/cmd/install.go:229:				installTargets				30.0%
github.com/ArmisSecurity/armis-cli/internal/cmd/install.go:380:				printCredentialStatus			100.0%
github.com/ArmisSecurity/armis-cli/internal/cmd/install_interactive.go:17:		runInteractiveInstall			0.0%
github.com/ArmisSecurity/armis-cli/internal/cmd/install_interactive.go:241:		collectCredentials			0.0%
github.com/ArmisSecurity/armis-cli/internal/cmd/install_interactive.go:352:		validateAndReport			0.0%
github.com/ArmisSecurity/armis-cli/internal/cmd/install_interactive.go:383:		selectEditorsWithCodex			0.0%
github.com/ArmisSecurity/armis-cli/internal/cmd/install_interactive.go:457:		offerHookSetup				0.0%
github.com/ArmisSecurity/armis-cli/internal/cmd/root.go:182:				SetVersion				100.0%
github.com/ArmisSecurity/armis-cli/internal/cmd/root.go:190:				Execute					100.0%
github.com/ArmisSecurity/armis-cli/internal/cmd/root.go:194:				init					100.0%
github.com/ArmisSecurity/armis-cli/internal/cmd/root.go:263:				fixedCompletions			100.0%
github.com/ArmisSecurity/armis-cli/internal/cmd/root.go:280:				formatCompletions			100.0%
github.com/ArmisSecurity/armis-cli/internal/cmd/root.go:289:				failOnCompletions			100.0%
github.com/ArmisSecurity/armis-cli/internal/cmd/root.go:303:				PrintUpdateNotification			81.2%
github.com/ArmisSecurity/armis-cli/internal/cmd/root.go:345:				printUpdateNotificationOnce		75.0%
github.com/ArmisSecurity/armis-cli/internal/cmd/root.go:358:				getEnvOrDefault				100.0%
github.com/ArmisSecurity/armis-cli/internal/cmd/root.go:365:				getEnvOrDefaultInt			100.0%
github.com/ArmisSecurity/armis-cli/internal/cmd/root.go:385:				getAPIBaseURL				100.0%
github.com/ArmisSecurity/armis-cli/internal/cmd/root.go:405:				clientOptionsForBaseURL			100.0%
github.com/ArmisSecurity/armis-cli/internal/cmd/root.go:432:				resolveDataPlaneURL			85.7%
github.com/ArmisSecurity/armis-cli/internal/cmd/root.go:464:				getAuthProvider				84.6%
github.com/ArmisSecurity/armis-cli/internal/cmd/root.go:506:				shouldAutoLoginSSO			100.0%
github.com/ArmisSecurity/armis-cli/internal/cmd/root.go:519:				storedAuthProvider			83.3%
github.com/ArmisSecurity/armis-cli/internal/cmd/root.go:545:				augmentNoCredentialsError		100.0%
github.com/ArmisSecurity/armis-cli/internal/cmd/root.go:555:				getPageLimit				100.0%
github.com/ArmisSecurity/armis-cli/internal/cmd/root.go:562:				validatePageLimit			100.0%
github.com/ArmisSecurity/armis-cli/internal/cmd/scan.go:38:				defaultFailOn				100.0%
github.com/ArmisSecurity/armis-cli/internal/cmd/scan.go:119:				init					100.0%
github.com/ArmisSecurity/armis-cli/internal/cmd/scan_image.go:185:			init					100.0%
github.com/ArmisSecurity/armis-cli/internal/cmd/scan_repo.go:204:			init					100.0%
github.com/ArmisSecurity/armis-cli/internal/cmd/supply_chain.go:17:			loadConfigUpward			80.0%
github.com/ArmisSecurity/armis-cli/internal/cmd/supply_chain.go:93:			init					100.0%
github.com/ArmisSecurity/armis-cli/internal/cmd/supply_chain_check.go:69:		init					100.0%
github.com/ArmisSecurity/armis-cli/internal/cmd/supply_chain_check.go:121:		runSupplyChainCheck			60.8%
github.com/ArmisSecurity/armis-cli/internal/cmd/supply_chain_check.go:314:		countNoun				100.0%
github.com/ArmisSecurity/armis-cli/internal/cmd/supply_chain_check.go:325:		countNounPlural				100.0%
github.com/ArmisSecurity/armis-cli/internal/cmd/supply_chain_check.go:332:		buildSummary				42.9%
github.com/ArmisSecurity/armis-cli/internal/cmd/supply_chain_check.go:349:		detectBaseLockfile			76.7%
github.com/ArmisSecurity/armis-cli/internal/cmd/supply_chain_check.go:430:		resolvePolicy				63.2%
github.com/ArmisSecurity/armis-cli/internal/cmd/supply_chain_init.go:72:		init					83.3%
github.com/ArmisSecurity/armis-cli/internal/cmd/supply_chain_init.go:84:		runSupplyChainInit			0.0%
github.com/ArmisSecurity/armis-cli/internal/cmd/supply_chain_init.go:120:		reportNothingInScope			0.0%
github.com/ArmisSecurity/armis-cli/internal/cmd/supply_chain_init.go:161:		detectWrappablePMs			95.5%
github.com/ArmisSecurity/armis-cli/internal/cmd/supply_chain_init.go:255:		summarizeDetectedPMs			100.0%
github.com/ArmisSecurity/armis-cli/internal/cmd/supply_chain_init.go:309:		powerShellSkippedDottedPMs		100.0%
github.com/ArmisSecurity/armis-cli/internal/cmd/supply_chain_init.go:338:		promptYesNo				0.0%
github.com/ArmisSecurity/armis-cli/internal/cmd/supply_chain_init.go:358:		confirmInteractive			0.0%
github.com/ArmisSecurity/armis-cli/internal/cmd/supply_chain_init.go:387:		readYesNo				100.0%
github.com/ArmisSecurity/armis-cli/internal/cmd/supply_chain_init.go:407:		runInitEnv				0.0%
github.com/ArmisSecurity/armis-cli/internal/cmd/supply_chain_init.go:421:		runInitNpmrc				75.0%
github.com/ArmisSecurity/armis-cli/internal/cmd/supply_chain_init.go:471:		runInitRC				0.0%
github.com/ArmisSecurity/armis-cli/internal/cmd/supply_chain_init.go:587:		runInitConfig				0.0%
github.com/ArmisSecurity/armis-cli/internal/cmd/supply_chain_init.go:662:		detectOrgScopes				100.0%
github.com/ArmisSecurity/armis-cli/internal/cmd/supply_chain_init.go:679:		collectScopesFromFile			89.5%
github.com/ArmisSecurity/armis-cli/internal/cmd/supply_chain_init.go:710:		extractScope				100.0%
github.com/ArmisSecurity/armis-cli/internal/cmd/supply_chain_report.go:110:		buildComplianceReport			78.1%
github.com/ArmisSecurity/armis-cli/internal/cmd/supply_chain_report.go:189:		transitivePolicyOrDefault		100.0%
github.com/ArmisSecurity/armis-cli/internal/cmd/supply_chain_report.go:199:		hours					66.7%
github.com/ArmisSecurity/armis-cli/internal/cmd/supply_chain_report.go:215:		writeComplianceReport			44.4%
github.com/ArmisSecurity/armis-cli/internal/cmd/supply_chain_status.go:43:		init					100.0%
github.com/ArmisSecurity/armis-cli/internal/cmd/supply_chain_status.go:54:		computeVerdict				100.0%
github.com/ArmisSecurity/armis-cli/internal/cmd/supply_chain_status.go:88:		collapsePipVariants			100.0%
github.com/ArmisSecurity/armis-cli/internal/cmd/supply_chain_status.go:112:		summarizePMs				100.0%
github.com/ArmisSecurity/armis-cli/internal/cmd/supply_chain_status.go:125:		uniqueWrappedPMs			62.5%
github.com/ArmisSecurity/armis-cli/internal/cmd/supply_chain_status.go:142:		runSupplyChainStatus			0.0%
github.com/ArmisSecurity/armis-cli/internal/cmd/supply_chain_status.go:250:		printEnvStatus				0.0%
github.com/ArmisSecurity/armis-cli/internal/cmd/supply_chain_status.go:267:		displayLockfilePath			0.0%
github.com/ArmisSecurity/armis-cli/internal/cmd/supply_chain_status.go:322:		runSupplyChainStatusJSON		82.4%
github.com/ArmisSecurity/armis-cli/internal/cmd/supply_chain_uninit.go:40:		init					100.0%
github.com/ArmisSecurity/armis-cli/internal/cmd/supply_chain_uninit.go:46:		runSupplyChainUninit			86.5%
github.com/ArmisSecurity/armis-cli/internal/cmd/supply_chain_wrap.go:61:		init					100.0%
github.com/ArmisSecurity/armis-cli/internal/cmd/supply_chain_wrap.go:78:		runSupplyChainWrap			100.0%
github.com/ArmisSecurity/armis-cli/internal/cmd/supply_chain_wrap.go:125:		canonicalPM				100.0%
github.com/ArmisSecurity/armis-cli/internal/cmd/supply_chain_wrap.go:132:		runProxyWrap				61.2%
github.com/ArmisSecurity/armis-cli/internal/cmd/supply_chain_wrap.go:277:		printWarnThroughSummary			94.1%
github.com/ArmisSecurity/armis-cli/internal/cmd/supply_chain_wrap.go:313:		execPM					0.0%
github.com/ArmisSecurity/armis-cli/internal/cmd/supply_chain_wrap.go:382:		exitWithCode				60.0%
github.com/ArmisSecurity/armis-cli/internal/cmd/supply_chain_wrap.go:408:		printBlockSummary			94.3%
github.com/ArmisSecurity/armis-cli/internal/cmd/supply_chain_wrap.go:580:		printFailureCulprits			100.0%
github.com/ArmisSecurity/armis-cli/internal/cmd/supply_chain_wrap.go:690:		skipCommand				77.8%
github.com/ArmisSecurity/armis-cli/internal/cmd/supply_chain_wrap.go:709:		firstCulpritName			85.7%
github.com/ArmisSecurity/armis-cli/internal/cmd/supply_chain_wrap.go:724:		quoteOrPlaceholder			66.7%
github.com/ArmisSecurity/armis-cli/internal/cmd/supply_chain_wrap.go:743:		ageToken				100.0%
github.com/ArmisSecurity/armis-cli/internal/cmd/supply_chain_wrap.go:751:		optionalAgeToken			100.0%
github.com/ArmisSecurity/armis-cli/internal/cmd/supply_chain_wrap.go:760:		rightPad				100.0%
github.com/ArmisSecurity/armis-cli/internal/cmd/supply_chain_wrap.go:788:		printPkgFilterLine			100.0%
github.com/ArmisSecurity/armis-cli/internal/cmd/supply_chain_wrap.go:839:		groupBlockedByPackage			85.7%
github.com/ArmisSecurity/armis-cli/internal/cmd/supply_chain_wrap.go:874:		checkedAllPass				100.0%
github.com/ArmisSecurity/armis-cli/internal/cmd/supply_chain_wrap.go:884:		formatPolicyShort			100.0%
github.com/ArmisSecurity/armis-cli/internal/cmd/supply_chain_wrap.go:902:		shouldShowRationale			100.0%
github.com/ArmisSecurity/armis-cli/internal/cmd/supply_chain_wrap.go:909:		rationaleAlreadyShown			80.0%
github.com/ArmisSecurity/armis-cli/internal/cmd/supply_chain_wrap.go:921:		markRationaleShown			66.7%
github.com/ArmisSecurity/armis-cli/internal/cmd/supply_chain_wrap.go:936:		filterRelevantBlocked			100.0%
github.com/ArmisSecurity/armis-cli/internal/cmd/supply_chain_wrap.go:956:		blockedDisplayVersion			100.0%
github.com/ArmisSecurity/armis-cli/internal/cmd/supply_chain_wrap.go:969:		allResultsPrerelease			100.0%
github.com/ArmisSecurity/armis-cli/internal/cmd/supply_chain_wrap.go:981:		severityDot				100.0%
github.com/ArmisSecurity/armis-cli/internal/cmd/supply_chain_wrap.go:985:		formatDurationShort			100.0%
github.com/ArmisSecurity/armis-cli/internal/cmd/supply_chain_wrap.go:998:		registryEnvForPM			100.0%
github.com/ArmisSecurity/armis-cli/internal/cmd/supply_chain_wrap.go:1040:		parseSkipPackages			75.0%
github.com/ArmisSecurity/armis-cli/internal/cmd/supply_chain_wrap.go:1062:		resolveWrapPolicy			87.5%
github.com/ArmisSecurity/armis-cli/internal/cmd/supply_chain_wrap.go:1082:		wrapEcosystemEnforced			85.7%
github.com/ArmisSecurity/armis-cli/internal/cmd/supply_chain_wrap.go:1114:		requiresPreInstallBlock			100.0%
github.com/ArmisSecurity/armis-cli/internal/cmd/supply_chain_wrap.go:1134:		uvProxySafe				100.0%
github.com/ArmisSecurity/armis-cli/internal/cmd/supply_chain_wrap.go:1145:		runPreInstallBlock			46.2%
github.com/ArmisSecurity/armis-cli/internal/cmd/supply_chain_wrap.go:1236:		writePreInstallReport			37.5%
github.com/ArmisSecurity/armis-cli/internal/cmd/supply_chain_wrap.go:1262:		printPreInstallBlockSummary		0.0%
github.com/ArmisSecurity/armis-cli/internal/cmd/supply_chain_wrap.go:1310:		blockedViolationNames			0.0%
github.com/ArmisSecurity/armis-cli/internal/cmd/supply_chain_wrap.go:1334:		normalizeProxyResidue			76.2%
github.com/ArmisSecurity/armis-cli/internal/cmd/supply_chain_wrap.go:1392:		uvCompileOutputFile			100.0%
github.com/ArmisSecurity/armis-cli/internal/cmd/supply_chain_wrap.go:1411:		uvToolReceipts				72.7%
github.com/ArmisSecurity/armis-cli/internal/cmd/supply_chain_wrap.go:1434:		pmToEcosystem				100.0%
github.com/ArmisSecurity/armis-cli/internal/cmd/supply_chain_wrap.go:1482:		checkGradleStaleness			100.0%
github.com/ArmisSecurity/armis-cli/internal/cmd/uninstall.go:43:			init					100.0%
github.com/ArmisSecurity/armis-cli/internal/cmd/uninstall.go:49:			runUninstall				0.0%
github.com/ArmisSecurity/armis-cli/internal/cmd/uninstall.go:68:			uninstallAll				38.9%
github.com/ArmisSecurity/armis-cli/internal/cmd/uninstall.go:226:			uninstallTargets			54.8%
github.com/ArmisSecurity/armis-cli/internal/cmd/uninstall.go:345:			confirm					100.0%
github.com/ArmisSecurity/armis-cli/internal/httpclient/client.go:31:			NewClient				92.3%
github.com/ArmisSecurity/armis-cli/internal/httpclient/client.go:64:			Do					86.1%
github.com/ArmisSecurity/armis-cli/internal/httpclient/transport.go:39:			ProxyAwareTransport			83.3%
github.com/ArmisSecurity/armis-cli/internal/httpclient/transport.go:52:			systemProxyFunc				100.0%
github.com/ArmisSecurity/armis-cli/internal/install/claude.go:23:			NewClaudeInstaller			75.0%
github.com/ArmisSecurity/armis-cli/internal/install/claude.go:35:			InstalledVersion			100.0%
github.com/ArmisSecurity/armis-cli/internal/install/claude.go:40:			Install					14.3%
github.com/ArmisSecurity/armis-cli/internal/install/claude.go:72:			pluginCacheDir				100.0%
github.com/ArmisSecurity/armis-cli/internal/install/claude.go:77:			PluginCacheDir				0.0%
github.com/ArmisSecurity/armis-cli/internal/install/claude.go:82:			EnvFilePath				100.0%
github.com/ArmisSecurity/armis-cli/internal/install/claude.go:87:			GetInstalledVersion			76.2%
github.com/ArmisSecurity/armis-cli/internal/install/claude.go:119:			HasExistingEnv				100.0%
github.com/ArmisSecurity/armis-cli/internal/install/claude.go:124:			registerMarketplace			83.3%
github.com/ArmisSecurity/armis-cli/internal/install/claude.go:141:			registerPlugin				75.0%
github.com/ArmisSecurity/armis-cli/internal/install/claude.go:170:			enablePlugin				100.0%
github.com/ArmisSecurity/armis-cli/internal/install/codex.go:18:			CodexConfigPath				66.7%
github.com/ArmisSecurity/armis-cli/internal/install/codex.go:26:			IsCodexDetected				80.0%
github.com/ArmisSecurity/armis-cli/internal/install/codex.go:37:			RegisterCodexMCP			78.6%
github.com/ArmisSecurity/armis-cli/internal/install/codex.go:71:			DeregisterCodexMCP			83.3%
github.com/ArmisSecurity/armis-cli/internal/install/codex.go:94:			buildCodexSection			100.0%
github.com/ArmisSecurity/armis-cli/internal/install/codex.go:107:			replaceTOMLSection			100.0%
github.com/ArmisSecurity/armis-cli/internal/install/codex.go:122:			removeTOMLSection			85.7%
github.com/ArmisSecurity/armis-cli/internal/install/codex.go:150:			findTOMLSectionBounds			100.0%
github.com/ArmisSecurity/armis-cli/internal/install/codex.go:204:			tomlQuote				100.0%
github.com/ArmisSecurity/armis-cli/internal/install/codex.go:210:			readFileOrEmpty				83.3%
github.com/ArmisSecurity/armis-cli/internal/install/codex.go:222:			writeFileAtomic				45.0%
github.com/ArmisSecurity/armis-cli/internal/install/editors.go:73:			EditorByID				100.0%
github.com/ArmisSecurity/armis-cli/internal/install/editors.go:86:			ConfigPath				100.0%
github.com/ArmisSecurity/armis-cli/internal/install/editors.go:97:			IsDetected				100.0%
github.com/ArmisSecurity/armis-cli/internal/install/editors.go:107:			Register				75.0%
github.com/ArmisSecurity/armis-cli/internal/install/editors.go:116:			DetectedEditors				100.0%
github.com/ArmisSecurity/armis-cli/internal/install/editors.go:133:			NewEditorInstaller			100.0%
github.com/ArmisSecurity/armis-cli/internal/install/editors.go:143:			InstalledVersion			100.0%
github.com/ArmisSecurity/armis-cli/internal/install/editors.go:146:			PluginDir				100.0%
github.com/ArmisSecurity/armis-cli/internal/install/editors.go:149:			EnvFilePath				100.0%
github.com/ArmisSecurity/armis-cli/internal/install/editors.go:152:			HasExistingEnv				100.0%
github.com/ArmisSecurity/armis-cli/internal/install/editors.go:163:			FetchPlugin				0.0%
github.com/ArmisSecurity/armis-cli/internal/install/editors.go:189:			GetInstalledVersion			80.0%
github.com/ArmisSecurity/armis-cli/internal/install/editors.go:199:			RegisterJetBrains			100.0%
github.com/ArmisSecurity/armis-cli/internal/install/editors.go:205:			defaultConfigPath			84.2%
github.com/ArmisSecurity/armis-cli/internal/install/editors.go:244:			homeDir					75.0%
github.com/ArmisSecurity/armis-cli/internal/install/editors.go:252:			appSupportPath				29.4%
github.com/ArmisSecurity/armis-cli/internal/install/editors.go:285:			registerEditor				100.0%
github.com/ArmisSecurity/armis-cli/internal/install/editors.go:299:			registerMCPServersFormat		100.0%
github.com/ArmisSecurity/armis-cli/internal/install/editors.go:313:			registerVSCodeFormat			100.0%
github.com/ArmisSecurity/armis-cli/internal/install/editors.go:332:			registerZedFormat			100.0%
github.com/ArmisSecurity/armis-cli/internal/install/editors.go:351:			stdServerEntry				100.0%
github.com/ArmisSecurity/armis-cli/internal/install/editors.go:358:			readJSONFileAsMap			100.0%
github.com/ArmisSecurity/armis-cli/internal/install/hooks.go:19:			InstallHooks				0.0%
github.com/ArmisSecurity/armis-cli/internal/install/hooks.go:30:			installHooksToFile			87.1%
github.com/ArmisSecurity/armis-cli/internal/install/hooks.go:100:			RemoveHooks				0.0%
github.com/ArmisSecurity/armis-cli/internal/install/hooks.go:111:			removeHooksFromFile			73.0%
github.com/ArmisSecurity/armis-cli/internal/install/hooks.go:173:			isArmisHookEntry			100.0%
github.com/ArmisSecurity/armis-cli/internal/install/hooks.go:185:			isArmisHookCommand			100.0%
github.com/ArmisSecurity/armis-cli/internal/install/manifest.go:44:			ManifestPath				100.0%
github.com/ArmisSecurity/armis-cli/internal/install/manifest.go:61:			ReadManifest				80.0%
github.com/ArmisSecurity/armis-cli/internal/install/manifest.go:79:			WriteManifest				66.7%
github.com/ArmisSecurity/armis-cli/internal/install/manifest.go:91:			NewManifest				100.0%
github.com/ArmisSecurity/armis-cli/internal/install/manifest.go:102:			AddEditor				66.7%
github.com/ArmisSecurity/armis-cli/internal/install/manifest.go:110:			RemoveEditor				100.0%
github.com/ArmisSecurity/armis-cli/internal/install/manifest.go:115:			SetClaude				100.0%
github.com/ArmisSecurity/armis-cli/internal/install/manifest.go:120:			SetCodex				0.0%
github.com/ArmisSecurity/armis-cli/internal/install/manifest.go:125:			ConfigFormat				100.0%
github.com/ArmisSecurity/armis-cli/internal/install/native_hooks.go:73:			HookClientByID				75.0%
github.com/ArmisSecurity/armis-cli/internal/install/native_hooks.go:83:			ConfigPath				100.0%
github.com/ArmisSecurity/armis-cli/internal/install/native_hooks.go:88:			IsDetected				80.0%
github.com/ArmisSecurity/armis-cli/internal/install/native_hooks.go:98:			DetectHookClients			100.0%
github.com/ArmisSecurity/armis-cli/internal/install/native_hooks.go:111:		hookConfigPath				75.0%
github.com/ArmisSecurity/armis-cli/internal/install/native_hooks.go:122:		InstallNativeHook			73.3%
github.com/ArmisSecurity/armis-cli/internal/install/native_hooks.go:153:		RemoveNativeHook			83.3%
github.com/ArmisSecurity/armis-cli/internal/install/native_hooks.go:165:		installClientHook			85.7%
github.com/ArmisSecurity/armis-cli/internal/install/native_hooks.go:182:		removeClientHook			66.7%
github.com/ArmisSecurity/armis-cli/internal/install/native_hooks.go:193:		cursorHooksPath				50.0%
github.com/ArmisSecurity/armis-cli/internal/install/native_hooks.go:199:		geminiHooksPath				50.0%
github.com/ArmisSecurity/armis-cli/internal/install/native_hooks.go:205:		codexHooksPath				50.0%
github.com/ArmisSecurity/armis-cli/internal/install/native_hooks.go:211:		copilotHooksPath			50.0%
github.com/ArmisSecurity/armis-cli/internal/install/native_hooks.go:217:		clineHooksPath				16.7%
github.com/ArmisSecurity/armis-cli/internal/install/native_hooks.go:241:		readJSONFileAsMapSafe			72.7%
github.com/ArmisSecurity/armis-cli/internal/install/native_hooks.go:262:		installMergedHook			82.4%
github.com/ArmisSecurity/armis-cli/internal/install/native_hooks.go:293:		removeMergedHook			0.0%
github.com/ArmisSecurity/armis-cli/internal/install/native_hooks.go:327:		installCursorHook			94.7%
github.com/ArmisSecurity/armis-cli/internal/install/native_hooks.go:362:		removeCursorHook			72.2%
github.com/ArmisSecurity/armis-cli/internal/install/native_hooks.go:398:		buildCursorHooks			100.0%
github.com/ArmisSecurity/armis-cli/internal/install/native_hooks.go:421:		buildGeminiHooks			100.0%
github.com/ArmisSecurity/armis-cli/internal/install/native_hooks.go:442:		buildCodexHooks				100.0%
github.com/ArmisSecurity/armis-cli/internal/install/native_hooks.go:473:		buildCopilotHooks			100.0%
github.com/ArmisSecurity/armis-cli/internal/install/native_hooks.go:490:		buildClineHooks				100.0%
github.com/ArmisSecurity/armis-cli/internal/install/native_hooks.go:508:		hasArmisHookEntries			100.0%
github.com/ArmisSecurity/armis-cli/internal/install/native_hooks.go:517:		filterNonArmisEntries			80.0%
github.com/ArmisSecurity/armis-cli/internal/install/native_hooks.go:527:		isArmisHookJSON				80.0%
github.com/ArmisSecurity/armis-cli/internal/install/native_hooks.go:545:		cleanupLegacyCopilotHook		42.9%
github.com/ArmisSecurity/armis-cli/internal/install/native_hooks.go:562:		removeLegacyFileIfArmisOnly		82.4%
github.com/ArmisSecurity/armis-cli/internal/install/native_hooks.go:592:		posixQuote				100.0%
github.com/ArmisSecurity/armis-cli/internal/install/native_hooks.go:596:		quotedCommand				100.0%
github.com/ArmisSecurity/armis-cli/internal/install/plugin.go:55:			newPluginInstaller			100.0%
github.com/ArmisSecurity/armis-cli/internal/install/plugin.go:63:			InstalledVersion			100.0%
github.com/ArmisSecurity/armis-cli/internal/install/plugin.go:68:			LatestVersion				0.0%
github.com/ArmisSecurity/armis-cli/internal/install/plugin.go:77:			FetchAndInstall				0.0%
github.com/ArmisSecurity/armis-cli/internal/install/plugin.go:99:			fetchLatestRelease			69.6%
github.com/ArmisSecurity/armis-cli/internal/install/plugin.go:139:			downloadAndExtract			73.6%
github.com/ArmisSecurity/armis-cli/internal/install/plugin.go:255:			createVenv				0.0%
github.com/ArmisSecurity/armis-cli/internal/install/plugin.go:298:			validateGitHubURL			100.0%
github.com/ArmisSecurity/armis-cli/internal/install/plugin.go:312:			extractFile				57.1%
github.com/ArmisSecurity/armis-cli/internal/install/plugin.go:324:			writeJSON				66.7%
github.com/ArmisSecurity/armis-cli/internal/install/plugin.go:340:			CheckPython				100.0%
github.com/ArmisSecurity/armis-cli/internal/install/plugin.go:350:			pythonNotFoundError			57.1%
github.com/ArmisSecurity/armis-cli/internal/install/plugin.go:370:			findPython				84.6%
github.com/ArmisSecurity/armis-cli/internal/install/plugin.go:398:			writeEnvFromEnvironment			85.7%
github.com/ArmisSecurity/armis-cli/internal/install/plugin.go:429:			WriteEnvFromValues			51.3%
github.com/ArmisSecurity/armis-cli/internal/install/plugin.go:497:			copyFile				63.6%
github.com/ArmisSecurity/armis-cli/internal/install/plugin.go:517:			venvPython				66.7%
github.com/ArmisSecurity/armis-cli/internal/install/precommit.go:25:			InstallPreCommit			71.4%
github.com/ArmisSecurity/armis-cli/internal/install/precommit.go:81:			RemovePreCommit				79.3%
github.com/ArmisSecurity/armis-cli/internal/install/precommit.go:135:			PreCommitHookPath			0.0%
github.com/ArmisSecurity/armis-cli/internal/install/precommit.go:144:			IsPreCommitInstalled			87.5%
github.com/ArmisSecurity/armis-cli/internal/install/precommit.go:160:			resolveHooksDir				39.1%
github.com/ArmisSecurity/armis-cli/internal/install/precommit.go:201:			DetectGitRoot				0.0%
github.com/ArmisSecurity/armis-cli/internal/install/precommit.go:210:			buildPreCommitSection			83.3%
github.com/ArmisSecurity/armis-cli/internal/install/uninstall.go:19:			NewUninstaller				100.0%
github.com/ArmisSecurity/armis-cli/internal/install/uninstall.go:28:			HasManifest				0.0%
github.com/ArmisSecurity/armis-cli/internal/install/uninstall.go:33:			PluginDir				0.0%
github.com/ArmisSecurity/armis-cli/internal/install/uninstall.go:38:			DeregisterEditor			0.0%
github.com/ArmisSecurity/armis-cli/internal/install/uninstall.go:59:			DeregisterAllEditors			80.6%
github.com/ArmisSecurity/armis-cli/internal/install/uninstall.go:110:			DeregisterClaude			64.7%
github.com/ArmisSecurity/armis-cli/internal/install/uninstall.go:144:			RemovePluginFiles			64.7%
github.com/ArmisSecurity/armis-cli/internal/install/uninstall.go:178:			editorConfigPath			0.0%
github.com/ArmisSecurity/armis-cli/internal/install/uninstall.go:189:			deregisterEditor			40.0%
github.com/ArmisSecurity/armis-cli/internal/install/uninstall.go:202:			deregisterFromFile			66.7%
github.com/ArmisSecurity/armis-cli/internal/install/uninstall.go:209:			deregisterMCPServersFormat		100.0%
github.com/ArmisSecurity/armis-cli/internal/install/uninstall.go:225:			deregisterVSCodeFormat			77.8%
github.com/ArmisSecurity/armis-cli/internal/install/uninstall.go:241:			deregisterZedFormat			77.8%
github.com/ArmisSecurity/armis-cli/internal/install/uninstall.go:257:			removeContinueFile			75.0%
github.com/ArmisSecurity/armis-cli/internal/install/uninstall.go:271:			removeFromMarketplace			100.0%
github.com/ArmisSecurity/armis-cli/internal/install/uninstall.go:276:			removeFromInstalledPlugins		100.0%
github.com/ArmisSecurity/armis-cli/internal/install/uninstall.go:281:			removeFromSettings			100.0%
github.com/ArmisSecurity/armis-cli/internal/install/uninstall.go:286:			removeJSONKey				55.6%
github.com/ArmisSecurity/armis-cli/internal/install/uninstall.go:301:			removeNestedJSONKey			61.5%
github.com/ArmisSecurity/armis-cli/internal/install/uninstall.go:321:			hasArmisEntry				83.3%
github.com/ArmisSecurity/armis-cli/internal/install/uninstall.go:343:			readAndParseJSON			100.0%
github.com/ArmisSecurity/armis-cli/internal/install/uninstall.go:355:			writeJSONAtomic				55.0%
github.com/ArmisSecurity/armis-cli/internal/install/validate.go:23:			ValidateCredentials			0.0%
github.com/ArmisSecurity/armis-cli/internal/install/validate.go:27:			resolveBaseURL				0.0%
github.com/ArmisSecurity/armis-cli/internal/install/validate.go:35:			validateCredentialsWithURL		100.0%
github.com/ArmisSecurity/armis-cli/internal/output/errno_unix.go:12:			isSyncNotSupported			100.0%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:62:				wrapText				100.0%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:85:				wrapLine				91.7%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:123:			formatRecommendations			100.0%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:193:			wrapTextWithFirstLinePrefix		90.9%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:232:			write					66.7%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:263:			Write					89.5%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:293:			Format					100.0%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:298:			FormatWithOptions			88.4%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:391:			SyncColors				100.0%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:395:			sortFindingsBySeverity			100.0%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:406:			loadSnippetFromFile			69.4%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:522:			formatCodeSnippetWithFrame		91.1%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:615:			truncatePlainLine			0.0%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:627:			highlightColumns			93.5%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:672:			scanDuration				89.5%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:705:			pluralize				100.0%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:714:			suppressionSummaryText			80.0%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:742:			renderBriefStatus			87.0%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:799:			renderSummaryDashboard			59.5%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:886:			renderFindings				88.9%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:915:			renderFinding				54.5%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:1027:			renderGroupedFindings			100.0%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:1051:			groupFindings				96.8%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:1108:			severityRank				100.0%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:1115:			isGitRepo				100.0%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:1122:			getGitBlame				38.1%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:1160:			parseGitBlame				95.2%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:1196:			maskEmail				100.0%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:1219:			getTopLevelDomain			75.0%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:1231:			getHumanDisplayTitle			100.0%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:1245:			wrapTitle				93.9%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:1304:			maskFixForDisplay			100.0%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:1339:			formatFixSection			0.0%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:1404:			formatProposedSnippet			0.0%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:1487:			limitHunkContext			64.7%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:1563:			parseDiffHunk				91.7%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:1585:			parseDiffLines				94.6%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:1676:			findInlineChanges			73.5%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:1747:			computeLCS				92.3%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:1799:			buildTokenPositions			100.0%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:1815:			tokenizeLine				92.9%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:1843:			isWordChar				100.0%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:1850:			formatDiffWithColorsStyled		77.1%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:1924:			extractDiffFilename			80.0%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:1946:			formatDiffHunkLine			100.0%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:1966:			formatDiffContextLine			100.0%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:1977:			formatDiffRemoveLine			86.4%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:2018:			formatDiffAddLine			86.4%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:2060:			applyInlineHighlights			81.0%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:2102:			truncateDiffLine			100.0%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:2109:			truncateDiffLineWithFlag		66.7%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:2123:			adjustHighlightSpans			83.3%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:2145:			groupDiffHunks				100.0%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:2176:			collectRenderOps			100.0%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:2219:			renderChangeBlock			100.0%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:2278:			formatDiffHunkSeparator			100.0%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:2293:			formatValidationSection			0.0%
github.com/ArmisSecurity/armis-cli/internal/output/human.go:2350:			getExposureDescription			0.0%
github.com/ArmisSecurity/armis-cli/internal/output/icons.go:25:				GetConfidenceIcon			100.0%
github.com/ArmisSecurity/armis-cli/internal/output/json.go:15:				Format					100.0%
github.com/ArmisSecurity/armis-cli/internal/output/json.go:24:				FormatWithOptions			66.7%
github.com/ArmisSecurity/armis-cli/internal/output/json.go:32:				formatWithDebug				0.0%
github.com/ArmisSecurity/armis-cli/internal/output/json.go:58:				maskScanResultForOutput			100.0%
github.com/ArmisSecurity/armis-cli/internal/output/json.go:78:				maskFindingSecrets			100.0%
github.com/ArmisSecurity/armis-cli/internal/output/junit.go:48:				Format					100.0%
github.com/ArmisSecurity/armis-cli/internal/output/junit.go:55:				FormatWithOptions			100.0%
github.com/ArmisSecurity/armis-cli/internal/output/junit.go:63:				formatWithSeverities			77.8%
github.com/ArmisSecurity/armis-cli/internal/output/junit.go:92:				isFailureSeverity			100.0%
github.com/ArmisSecurity/armis-cli/internal/output/junit.go:102:			convertToJUnitCasesWithSeverities	100.0%
github.com/ArmisSecurity/armis-cli/internal/output/junit.go:135:			countFailuresWithSeverities		100.0%
github.com/ArmisSecurity/armis-cli/internal/output/output.go:26:			Error					0.0%
github.com/ArmisSecurity/armis-cli/internal/output/output.go:37:			Error					0.0%
github.com/ArmisSecurity/armis-cli/internal/output/output.go:58:			GetFormatter				100.0%
github.com/ArmisSecurity/armis-cli/internal/output/output.go:75:			ShouldFail				100.0%
github.com/ArmisSecurity/armis-cli/internal/output/output.go:94:			FilterActiveFindings			100.0%
github.com/ArmisSecurity/armis-cli/internal/output/output.go:107:			CheckExit				100.0%
github.com/ArmisSecurity/armis-cli/internal/output/sarif.go:174:			normalizeCWE				100.0%
github.com/ArmisSecurity/armis-cli/internal/output/sarif.go:183:			normalizeCVE				100.0%
github.com/ArmisSecurity/armis-cli/internal/output/sarif.go:193:			stripMarkdown				100.0%
github.com/ArmisSecurity/armis-cli/internal/output/sarif.go:205:			Format					100.0%
github.com/ArmisSecurity/armis-cli/internal/output/sarif.go:232:			firstNonEmpty				100.0%
github.com/ArmisSecurity/armis-cli/internal/output/sarif.go:251:			stableRuleID				100.0%
github.com/ArmisSecurity/armis-cli/internal/output/sarif.go:269:			buildRules				96.3%
github.com/ArmisSecurity/armis-cli/internal/output/sarif.go:340:			convertToSarifResults			90.3%
github.com/ArmisSecurity/armis-cli/internal/output/sarif.go:441:			buildMessageText			100.0%
github.com/ArmisSecurity/armis-cli/internal/output/sarif.go:448:			severityToSarifLevel			100.0%
github.com/ArmisSecurity/armis-cli/internal/output/sarif.go:468:			severityToSecurityScore			100.0%
github.com/ArmisSecurity/armis-cli/internal/output/sarif.go:487:			generateHelpURI				100.0%
github.com/ArmisSecurity/armis-cli/internal/output/sarif.go:511:			convertFixToSarif			90.5%
github.com/ArmisSecurity/armis-cli/internal/output/sarif.go:628:			FormatWithOptions			100.0%
github.com/ArmisSecurity/armis-cli/internal/output/styles.go:148:			DefaultStyles				100.0%
github.com/ArmisSecurity/armis-cli/internal/output/styles.go:286:			NoColorStyles				100.0%
github.com/ArmisSecurity/armis-cli/internal/output/styles.go:363:			GetStyles				100.0%
github.com/ArmisSecurity/armis-cli/internal/output/styles.go:371:			SyncStylesWithColorMode			100.0%
github.com/ArmisSecurity/armis-cli/internal/output/styles.go:396:			GetSeverityText				100.0%
github.com/ArmisSecurity/armis-cli/internal/output/styles.go:423:			RenderCodeBlock				100.0%
github.com/ArmisSecurity/armis-cli/internal/output/styles.go:448:			TerminalWidth				33.3%
github.com/ArmisSecurity/armis-cli/internal/output/syntax.go:21:			GetLexer				100.0%
github.com/ArmisSecurity/armis-cli/internal/output/syntax.go:32:			GetChromaStyle				80.0%
github.com/ArmisSecurity/armis-cli/internal/output/syntax.go:45:			HighlightCode				81.2%
github.com/ArmisSecurity/armis-cli/internal/output/syntax.go:79:			HighlightLine				75.0%
github.com/ArmisSecurity/armis-cli/internal/output/syntax.go:88:			getTerminalFormatter			60.0%
github.com/ArmisSecurity/armis-cli/internal/output/syntax.go:103:			HighlightLineWithBackground		87.5%
github.com/ArmisSecurity/armis-cli/internal/output/syntax.go:126:			getBackgroundANSI			58.3%
github.com/ArmisSecurity/armis-cli/internal/output/syntax.go:158:			rgbToANSI256				0.0%
github.com/ArmisSecurity/armis-cli/internal/output/syntax.go:171:			parseHexColor				76.9%
github.com/ArmisSecurity/armis-cli/internal/output/writer.go:51:			validateOutputPath			92.3%
github.com/ArmisSecurity/armis-cli/internal/output/writer.go:88:			NewFileOutput				88.2%
github.com/ArmisSecurity/armis-cli/internal/output/writer.go:145:			Writer					100.0%
github.com/ArmisSecurity/armis-cli/internal/output/writer.go:150:			Close					100.0%
github.com/ArmisSecurity/armis-cli/internal/output/writer.go:167:			FormatFromExtension			100.0%
github.com/ArmisSecurity/armis-cli/internal/progress/progress.go:32:			IsCI					100.0%
github.com/ArmisSecurity/armis-cli/internal/progress/progress.go:60:			isTerminalWriter			100.0%
github.com/ArmisSecurity/armis-cli/internal/progress/progress.go:68:			NewReader				100.0%
github.com/ArmisSecurity/armis-cli/internal/progress/progress.go:83:			NewWriter				50.0%
github.com/ArmisSecurity/armis-cli/internal/progress/progress.go:117:			NewSpinner				100.0%
github.com/ArmisSecurity/armis-cli/internal/progress/progress.go:125:			NewSpinnerWithTimeout			100.0%
github.com/ArmisSecurity/armis-cli/internal/progress/progress.go:142:			NewSpinnerWithContext			100.0%
github.com/ArmisSecurity/armis-cli/internal/progress/progress.go:150:			SetWriter				100.0%
github.com/ArmisSecurity/armis-cli/internal/progress/progress.go:159:			Start					89.8%
github.com/ArmisSecurity/armis-cli/internal/progress/progress.go:275:			Stop					100.0%
github.com/ArmisSecurity/armis-cli/internal/progress/progress.go:310:			Update					100.0%
github.com/ArmisSecurity/armis-cli/internal/progress/progress.go:317:			GetElapsed				100.0%
github.com/ArmisSecurity/armis-cli/internal/progress/progress.go:324:			formatDuration				100.0%
github.com/ArmisSecurity/armis-cli/internal/scan/exploitability.go:42:			ShouldFilterByExploitability		100.0%
github.com/ArmisSecurity/armis-cli/internal/scan/exploitability.go:60:			exploitabilityLevel			80.0%
github.com/ArmisSecurity/armis-cli/internal/scan/finding_type.go:9:			DeriveFindingType			100.0%
github.com/ArmisSecurity/armis-cli/internal/scan/image/image.go:53:			NewScanner				100.0%
github.com/ArmisSecurity/armis-cli/internal/scan/image/image.go:68:			WithPollInterval			100.0%
github.com/ArmisSecurity/armis-cli/internal/scan/image/image.go:74:			WithFetchRetryInterval			100.0%
github.com/ArmisSecurity/armis-cli/internal/scan/image/image.go:80:			WithSBOMVEXOptions			0.0%
github.com/ArmisSecurity/armis-cli/internal/scan/image/image.go:87:			WithPullPolicy				0.0%
github.com/ArmisSecurity/armis-cli/internal/scan/image/image.go:93:			ScanImage				0.0%
github.com/ArmisSecurity/armis-cli/internal/scan/image/image.go:124:			ScanTarball				76.9%
github.com/ArmisSecurity/armis-cli/internal/scan/image/image.go:243:			exportImage				0.0%
github.com/ArmisSecurity/armis-cli/internal/scan/image/image.go:301:			IsDockerAvailable			42.9%
github.com/ArmisSecurity/armis-cli/internal/scan/image/image.go:316:			getDockerCommand			75.0%
github.com/ArmisSecurity/armis-cli/internal/scan/image/image.go:325:			validateDockerCommand			100.0%
github.com/ArmisSecurity/armis-cli/internal/scan/image/image.go:334:			imageExistsLocally			87.5%
github.com/ArmisSecurity/armis-cli/internal/scan/image/image.go:349:			determinePullBehavior			100.0%
github.com/ArmisSecurity/armis-cli/internal/scan/image/image.go:367:			isRetryableError			75.0%
github.com/ArmisSecurity/armis-cli/internal/scan/image/image.go:375:			buildScanResult				100.0%
github.com/ArmisSecurity/armis-cli/internal/scan/image/image.go:402:			convertNormalizedFindings		85.0%
github.com/ArmisSecurity/armis-cli/internal/scan/image/image.go:525:			cleanDescription			100.0%
github.com/ArmisSecurity/armis-cli/internal/scan/image/image.go:544:			isEmptyFinding				100.0%
github.com/ArmisSecurity/armis-cli/internal/scan/image/image.go:559:			generateFindingTitle			100.0%
github.com/ArmisSecurity/armis-cli/internal/scan/image/validate.go:11:			validateImageName			100.0%
github.com/ArmisSecurity/armis-cli/internal/scan/mask.go:22:				MaskFixSecrets				100.0%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/files.go:26:			ParseFileList				87.5%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/files.go:41:			addFile					87.0%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/files.go:94:			Files					100.0%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/files.go:99:			RepoRoot				100.0%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/files.go:104:			ValidateExistence			100.0%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/gitchanges.go:52:			GitChangedFiles				82.6%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/gitchanges.go:103:		gitRepoRoot				80.0%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/gitchanges.go:128:		changedUncommitted			41.7%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/gitchanges.go:157:		changedStaged				75.0%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/gitchanges.go:170:		validateRef				100.0%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/gitchanges.go:183:		changedSinceRef				75.0%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/gitchanges.go:206:		filterToScanPath			95.8%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/gitchanges.go:259:		runGit					91.7%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/gitchanges.go:286:		parseLines				100.0%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/gitchanges.go:306:		combineAndDedupe			100.0%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/ignore.go:28:			LoadIgnorePatterns			100.0%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/ignore.go:36:			LoadSuppressionConfig			0.0%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/ignore.go:78:			LoadArmisIgnore				92.9%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/ignore.go:138:			parseArmisIgnoreFile			92.5%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/ignore.go:204:			Match					100.0%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/ignore.go:216:			shouldSkipDir				100.0%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/inline.go:102:			ApplyInlineSuppression			97.2%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/inline.go:231:			parseInlineComment			100.0%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/inline.go:258:			isCommentLine				100.0%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/inline.go:272:			isFuncSignature				100.0%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/inline.go:285:			containsAny				100.0%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/inline.go:299:			findCommentStart			83.3%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/inline.go:339:			parseDirectiveParams			93.9%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/inline.go:397:			matchesInlineDirective			100.0%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/inline.go:433:			buildInlineSuppressionInfo		100.0%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/inline.go:461:			countSuppressed				0.0%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/matcher.go:28:			MatchFinding				100.0%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/matcher.go:62:			cweMatches				100.0%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/matcher.go:78:			ApplySuppression			100.0%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/matcher.go:101:			recomputeSummary			100.0%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/repo.go:46:			NewScanner				100.0%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/repo.go:61:			WithPollInterval			100.0%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/repo.go:67:			WithFetchRetryInterval			100.0%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/repo.go:73:			WithIncludeFiles			0.0%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/repo.go:79:			WithSBOMVEXOptions			0.0%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/repo.go:85:			Scan					67.2%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/repo.go:315:			tarGzDirectory				71.8%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/repo.go:397:			isPathContained				75.0%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/repo.go:406:			tarGzFiles				78.6%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/repo.go:497:			safeAddSize				100.0%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/repo.go:504:			calculateFilesSize			78.6%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/repo.go:530:			calculateDirSize			76.9%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/repo.go:577:			shouldSkip				100.0%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/repo.go:608:			isTestFile				100.0%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/repo.go:654:			isRetryableError			75.0%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/repo.go:663:			buildScanResult				100.0%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/repo.go:690:			convertNormalizedFindings		73.3%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/repo.go:813:			cleanDescription			100.0%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/repo.go:834:			generateFindingTitle			100.0%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/repo.go:838:			isEmptyFinding				100.0%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/suppression.go:58:		NewSuppressionConfig			100.0%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/suppression.go:63:		IsEmpty					100.0%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/suppression.go:77:		Add					100.0%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/suppression.go:99:		CategoryMapping				100.0%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/suppression.go:112:		parseDirectiveLine			93.5%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/suppression.go:173:		hasDirectivePrefix			100.0%
github.com/ArmisSecurity/armis-cli/internal/scan/repo/suppression.go:187:		validateCWE				100.0%
github.com/ArmisSecurity/armis-cli/internal/scan/sbom_vex.go:38:			NewSBOMVEXDownloader			100.0%
github.com/ArmisSecurity/armis-cli/internal/scan/sbom_vex.go:50:			Download				85.2%
github.com/ArmisSecurity/armis-cli/internal/scan/sbom_vex.go:102:			downloadAndSave				77.8%
github.com/ArmisSecurity/armis-cli/internal/scan/status.go:16:				FormatScanStatus			100.0%
github.com/ArmisSecurity/armis-cli/internal/scan/status.go:35:				FormatElapsed				100.0%
github.com/ArmisSecurity/armis-cli/internal/scan/status.go:48:				MapSeverity				100.0%
github.com/ArmisSecurity/armis-cli/internal/scan/testhelpers/findings.go:9:		CreateNormalizedFinding			0.0%
github.com/ArmisSecurity/armis-cli/internal/scan/testhelpers/findings.go:14:		CreateNormalizedFindingWithLabels	0.0%
github.com/ArmisSecurity/armis-cli/internal/scan/testhelpers/findings.go:19:		CreateNormalizedFindingFull		0.0%
github.com/ArmisSecurity/armis-cli/internal/scan/testhelpers/tarball.go:32:		WriteMinimalTar				64.3%
github.com/ArmisSecurity/armis-cli/internal/scan/testhelpers/tarball.go:68:		assertPathInsideTempDir			80.0%
github.com/ArmisSecurity/armis-cli/internal/scan/title.go:14:				GenerateFindingTitle			100.0%
github.com/ArmisSecurity/armis-cli/internal/scan/validate.go:21:			HasAllowedTarExtension			100.0%
github.com/ArmisSecurity/armis-cli/internal/scan/validate.go:44:			ValidateTarballFormat			89.7%
github.com/ArmisSecurity/armis-cli/internal/scan/validate.go:114:			isGzip					100.0%
github.com/ArmisSecurity/armis-cli/internal/scan/validate.go:121:			isUstar					100.0%
github.com/ArmisSecurity/armis-cli/internal/scan/validate.go:133:			ValidateUploadSize			100.0%
github.com/ArmisSecurity/armis-cli/internal/scan/validate.go:152:			FormatBytes				100.0%
github.com/ArmisSecurity/armis-cli/internal/supplychain/check/bun.go:18:		ParseBunLockfile			80.0%
github.com/ArmisSecurity/armis-cli/internal/supplychain/check/bun.go:62:		parseBunPackageKey			80.0%
github.com/ArmisSecurity/armis-cli/internal/supplychain/check/bun.go:76:		shouldSkipBunPackage			83.3%
github.com/ArmisSecurity/armis-cli/internal/supplychain/check/check.go:35:		RunCheck				0.0%
github.com/ArmisSecurity/armis-cli/internal/supplychain/check/check.go:39:		runCheck				96.7%
github.com/ArmisSecurity/armis-cli/internal/supplychain/check/check.go:106:		parseLockfile				33.3%
github.com/ArmisSecurity/armis-cli/internal/supplychain/check/check.go:133:		queryRegistry				0.0%
github.com/ArmisSecurity/armis-cli/internal/supplychain/check/check.go:151:		DetectEcosystemFromPath			0.0%
github.com/ArmisSecurity/armis-cli/internal/supplychain/check/check.go:155:		detectEcosystemFromPath			92.3%
github.com/ArmisSecurity/armis-cli/internal/supplychain/check/check.go:191:		isRequirementsFile			100.0%
github.com/ArmisSecurity/armis-cli/internal/supplychain/check/check.go:203:		diffEntries				100.0%
github.com/ArmisSecurity/armis-cli/internal/supplychain/check/gradle.go:16:		ParseGradleLockfile			90.3%
github.com/ArmisSecurity/armis-cli/internal/supplychain/check/lockfile.go:24:		readLockfile				90.0%
github.com/ArmisSecurity/armis-cli/internal/supplychain/check/maven.go:38:		ParseMavenDeps				90.5%
github.com/ArmisSecurity/armis-cli/internal/supplychain/check/maven.go:79:		mavenDepToEntry				87.5%
github.com/ArmisSecurity/armis-cli/internal/supplychain/check/npm.go:34:		ParseNPMLockfile			88.0%
github.com/ArmisSecurity/armis-cli/internal/supplychain/check/npm.go:89:		extractPackageName			100.0%
github.com/ArmisSecurity/armis-cli/internal/supplychain/check/npm.go:97:		shouldSkipResolved			83.3%
github.com/ArmisSecurity/armis-cli/internal/supplychain/check/pdm.go:26:		ParsePDMLockfile			85.7%
github.com/ArmisSecurity/armis-cli/internal/supplychain/check/pdm.go:64:		shouldSkipPDMSource			100.0%
github.com/ArmisSecurity/armis-cli/internal/supplychain/check/pip.go:29:		ParsePipRequirements			90.5%
github.com/ArmisSecurity/armis-cli/internal/supplychain/check/pip.go:79:		parsePipRequirement			100.0%
github.com/ArmisSecurity/armis-cli/internal/supplychain/check/pip.go:106:		shouldSkipPipLine			85.7%
github.com/ArmisSecurity/armis-cli/internal/supplychain/check/pip.go:126:		normalizePipName			100.0%
github.com/ArmisSecurity/armis-cli/internal/supplychain/check/pipfile.go:20:		ParsePipfileLock			94.7%
github.com/ArmisSecurity/armis-cli/internal/supplychain/check/pipfile.go:55:		pipfileEntryToPackage			80.0%
github.com/ArmisSecurity/armis-cli/internal/supplychain/check/pnpm.go:28:		ParsePNPMLockfile			82.4%
github.com/ArmisSecurity/armis-cli/internal/supplychain/check/pnpm.go:65:		parsePnpmPackageKey			79.2%
github.com/ArmisSecurity/armis-cli/internal/supplychain/check/pnpm.go:116:		stripPeerFromKey			100.0%
github.com/ArmisSecurity/armis-cli/internal/supplychain/check/pnpm.go:147:		shouldSkipPnpmPackage			87.5%
github.com/ArmisSecurity/armis-cli/internal/supplychain/check/poetry.go:26:		ParsePoetryLockfile			85.7%
github.com/ArmisSecurity/armis-cli/internal/supplychain/check/poetry.go:58:		shouldSkipPoetrySource			100.0%
github.com/ArmisSecurity/armis-cli/internal/supplychain/check/uv.go:26:			ParseUVLockfile				85.7%
github.com/ArmisSecurity/armis-cli/internal/supplychain/check/uv.go:64:			shouldSkipUVSource			100.0%
github.com/ArmisSecurity/armis-cli/internal/supplychain/check/yarn.go:15:		ParseYarnLockfile			100.0%
github.com/ArmisSecurity/armis-cli/internal/supplychain/check/yarn.go:28:		isBerryLockfile				100.0%
github.com/ArmisSecurity/armis-cli/internal/supplychain/check/yarn.go:37:		parseYarnBerry				81.0%
github.com/ArmisSecurity/armis-cli/internal/supplychain/check/yarn.go:79:		extractBerryPackageName			30.0%
github.com/ArmisSecurity/armis-cli/internal/supplychain/check/yarn.go:119:		shouldSkipYarnResolution		100.0%
github.com/ArmisSecurity/armis-cli/internal/supplychain/check/yarn.go:138:		parseYarnClassic			96.0%
github.com/ArmisSecurity/armis-cli/internal/supplychain/check/yarn.go:191:		extractClassicPackageName		75.0%
github.com/ArmisSecurity/armis-cli/internal/supplychain/check/yarn.go:199:		shouldSkipClassicProtocol		100.0%
github.com/ArmisSecurity/armis-cli/internal/supplychain/config.go:64:			KnownEcosystemsHint			0.0%
github.com/ArmisSecurity/armis-cli/internal/supplychain/config.go:72:			LoadConfig				88.2%
github.com/ArmisSecurity/armis-cli/internal/supplychain/config.go:105:			ToPolicy				100.0%
github.com/ArmisSecurity/armis-cli/internal/supplychain/config.go:136:			UnknownEcosystems			100.0%
github.com/ArmisSecurity/armis-cli/internal/supplychain/config.go:159:			EnforcesEcosystem			100.0%
github.com/ArmisSecurity/armis-cli/internal/supplychain/config.go:190:			FindConfigDir				91.7%
github.com/ArmisSecurity/armis-cli/internal/supplychain/detect.go:64:			DetectEcosystems			100.0%
github.com/ArmisSecurity/armis-cli/internal/supplychain/detect.go:109:			DetectEcosystemsUpward			92.3%
github.com/ArmisSecurity/armis-cli/internal/supplychain/detect.go:151:			FindEcosystemLockfile			100.0%
github.com/ArmisSecurity/armis-cli/internal/supplychain/detect.go:170:			FindUpward				92.3%
github.com/ArmisSecurity/armis-cli/internal/supplychain/detect.go:197:			ecosystemLockfileName			100.0%
github.com/ArmisSecurity/armis-cli/internal/supplychain/manifest.go:27:			DirectDependencies			100.0%
github.com/ArmisSecurity/armis-cli/internal/supplychain/manifest.go:54:			directDepsFromPackageJSON		86.4%
github.com/ArmisSecurity/armis-cli/internal/supplychain/normalize.go:37:		NormalizeArtifact			50.0%
github.com/ArmisSecurity/armis-cli/internal/supplychain/normalize.go:141:		DetectLoopbackRegistry			100.0%
github.com/ArmisSecurity/armis-cli/internal/supplychain/normalize.go:156:		FileContainsString			85.7%
github.com/ArmisSecurity/armis-cli/internal/supplychain/npmrc.go:39:			isNpmrcMarkerLine			100.0%
github.com/ArmisSecurity/armis-cli/internal/supplychain/npmrc.go:50:			HasNpmrcMarker				100.0%
github.com/ArmisSecurity/armis-cli/internal/supplychain/npmrc.go:63:			NpmrcFileHasMarker			100.0%
github.com/ArmisSecurity/armis-cli/internal/supplychain/npmrc.go:77:			RemoveNpmrcMarker			85.7%
github.com/ArmisSecurity/armis-cli/internal/supplychain/npmrc.go:111:			removeNpmrcMarkerLines			100.0%
github.com/ArmisSecurity/armis-cli/internal/supplychain/proxy.go:172:			NewProxy				95.5%
github.com/ArmisSecurity/armis-cli/internal/supplychain/proxy.go:244:			Start					91.7%
github.com/ArmisSecurity/armis-cli/internal/supplychain/proxy.go:270:			Addr					100.0%
github.com/ArmisSecurity/armis-cli/internal/supplychain/proxy.go:289:			Upstream				100.0%
github.com/ArmisSecurity/armis-cli/internal/supplychain/proxy.go:297:			Blocked					100.0%
github.com/ArmisSecurity/armis-cli/internal/supplychain/proxy.go:305:			Checked					100.0%
github.com/ArmisSecurity/armis-cli/internal/supplychain/proxy.go:311:			Allowed					100.0%
github.com/ArmisSecurity/armis-cli/internal/supplychain/proxy.go:324:			Warned					100.0%
github.com/ArmisSecurity/armis-cli/internal/supplychain/proxy.go:338:			isTransitive				100.0%
github.com/ArmisSecurity/armis-cli/internal/supplychain/proxy.go:350:			warnThroughTransitive			100.0%
github.com/ArmisSecurity/armis-cli/internal/supplychain/proxy.go:354:			Close					66.7%
github.com/ArmisSecurity/armis-cli/internal/supplychain/proxy.go:361:			handleRequest				100.0%
github.com/ArmisSecurity/armis-cli/internal/supplychain/proxy.go:389:			handleMetadataFiltering			72.7%
github.com/ArmisSecurity/armis-cli/internal/supplychain/proxy.go:514:			copyCacheHeaders			100.0%
github.com/ArmisSecurity/armis-cli/internal/supplychain/proxy.go:536:			sanitizeHeaderValue			100.0%
github.com/ArmisSecurity/armis-cli/internal/supplychain/proxy.go:540:			filterMetadata				93.5%
github.com/ArmisSecurity/armis-cli/internal/supplychain/proxy.go:742:			recordConstraintData			96.0%
github.com/ArmisSecurity/armis-cli/internal/supplychain/proxy.go:854:			recordWarned				71.4%
github.com/ArmisSecurity/armis-cli/internal/supplychain/proxy.go:882:			filterPyPISimple			91.8%
github.com/ArmisSecurity/armis-cli/internal/supplychain/proxy.go:984:			pypiFileAge				88.9%
github.com/ArmisSecurity/armis-cli/internal/supplychain/proxy.go:1002:			pypiVersionFromFilename			100.0%
github.com/ArmisSecurity/armis-cli/internal/supplychain/proxy.go:1038:			jsonString				83.3%
github.com/ArmisSecurity/armis-cli/internal/supplychain/proxy.go:1049:			reverseProxy				100.0%
github.com/ArmisSecurity/armis-cli/internal/supplychain/proxy.go:1053:			extractPackageNameFromPath		91.7%
github.com/ArmisSecurity/armis-cli/internal/supplychain/proxy.go:1087:			isMetadataRequest			100.0%
github.com/ArmisSecurity/armis-cli/internal/supplychain/proxy.go:1130:			IsPrerelease				100.0%
github.com/ArmisSecurity/armis-cli/internal/supplychain/proxy.go:1141:			extractPyPIPackageNameFromPath		100.0%
github.com/ArmisSecurity/armis-cli/internal/supplychain/proxy.go:1166:			isPyPIMetadataRequest			100.0%
github.com/ArmisSecurity/armis-cli/internal/supplychain/proxy_constraints.go:42:	EvaluateConstraints			89.1%
github.com/ArmisSecurity/armis-cli/internal/supplychain/proxy_constraints.go:133:	isWildcardRange				100.0%
github.com/ArmisSecurity/armis-cli/internal/supplychain/proxy_constraints.go:145:	parseVersions				100.0%
github.com/ArmisSecurity/armis-cli/internal/supplychain/proxy_constraints.go:159:	anySatisfies				100.0%
github.com/ArmisSecurity/armis-cli/internal/supplychain/registry/maven.go:49:		NewMavenClient				100.0%
github.com/ArmisSecurity/armis-cli/internal/supplychain/registry/maven.go:61:		NewMavenClientWithHTTP			66.7%
github.com/ArmisSecurity/armis-cli/internal/supplychain/registry/maven.go:71:		GetPublishDate				96.3%
github.com/ArmisSecurity/armis-cli/internal/supplychain/registry/maven.go:125:		escapeSolrQueryValue			100.0%
github.com/ArmisSecurity/armis-cli/internal/supplychain/registry/maven.go:131:		fetchPublishDate			76.9%
github.com/ArmisSecurity/armis-cli/internal/supplychain/registry/maven.go:185:		GetPublishDates				100.0%
github.com/ArmisSecurity/armis-cli/internal/supplychain/registry/npm.go:70:		NewClient				100.0%
github.com/ArmisSecurity/armis-cli/internal/supplychain/registry/npm.go:85:		NewClientWithHTTP			66.7%
github.com/ArmisSecurity/armis-cli/internal/supplychain/registry/npm.go:95:		GetPublishDate				86.7%
github.com/ArmisSecurity/armis-cli/internal/supplychain/registry/npm.go:126:		GetPublishDates				100.0%
github.com/ArmisSecurity/armis-cli/internal/supplychain/registry/npm.go:156:		fetchMetadata				85.7%
github.com/ArmisSecurity/armis-cli/internal/supplychain/registry/pypi.go:49:		NewPyPIClient				100.0%
github.com/ArmisSecurity/armis-cli/internal/supplychain/registry/pypi.go:61:		NewPyPIClientWithHTTP			100.0%
github.com/ArmisSecurity/armis-cli/internal/supplychain/registry/pypi.go:77:		GetPublishDate				79.2%
github.com/ArmisSecurity/armis-cli/internal/supplychain/registry/pypi.go:126:		GetPublishDates				100.0%
github.com/ArmisSecurity/armis-cli/internal/supplychain/registry/pypi.go:156:		fetchReleases				82.1%
github.com/ArmisSecurity/armis-cli/internal/supplychain/registry/pypi.go:218:		NormalizePyPIName			100.0%
github.com/ArmisSecurity/armis-cli/internal/supplychain/registry/pypi.go:222:		normalizePyPIName			100.0%
github.com/ArmisSecurity/armis-cli/internal/supplychain/registry/pypi.go:230:		lookupReleaseNormalized			100.0%
github.com/ArmisSecurity/armis-cli/internal/supplychain/registry/pypi.go:246:		normalizeVersion			84.6%
github.com/ArmisSecurity/armis-cli/internal/supplychain/shell.go:68:			sanitizePMNames				100.0%
github.com/ArmisSecurity/armis-cli/internal/supplychain/shell.go:86:			DetectShells				93.3%
github.com/ArmisSecurity/armis-cli/internal/supplychain/shell.go:138:			powershellProfiles			33.3%
github.com/ArmisSecurity/armis-cli/internal/supplychain/shell.go:170:			resolveWindowsDocumentsDir		0.0%
github.com/ArmisSecurity/armis-cli/internal/supplychain/shell.go:192:			GenerateWrapper				100.0%
github.com/ArmisSecurity/armis-cli/internal/supplychain/shell.go:208:			ShellReloadCommand			100.0%
github.com/ArmisSecurity/armis-cli/internal/supplychain/shell.go:222:			IsPowerShell				0.0%
github.com/ArmisSecurity/armis-cli/internal/supplychain/shell.go:228:			generatePosixWrapper			100.0%
github.com/ArmisSecurity/armis-cli/internal/supplychain/shell.go:262:			generateFishWrapper			100.0%
github.com/ArmisSecurity/armis-cli/internal/supplychain/shell.go:299:			generatePowerShellWrapper		100.0%
github.com/ArmisSecurity/armis-cli/internal/supplychain/shell.go:344:			shellQuote				100.0%
github.com/ArmisSecurity/armis-cli/internal/supplychain/shell.go:353:			shellQuotePowerShell			100.0%
github.com/ArmisSecurity/armis-cli/internal/supplychain/shell.go:374:			resolveCliPath				77.8%
github.com/ArmisSecurity/armis-cli/internal/supplychain/shell.go:390:			InjectFunctions				88.9%
github.com/ArmisSecurity/armis-cli/internal/supplychain/shell.go:405:			injectIntoFile				78.9%
github.com/ArmisSecurity/armis-cli/internal/supplychain/shell.go:441:			RemoveFunctions				87.5%
github.com/ArmisSecurity/armis-cli/internal/supplychain/shell.go:455:			removeFromFile				86.7%
github.com/ArmisSecurity/armis-cli/internal/supplychain/shell.go:483:			removeBlock				100.0%
github.com/ArmisSecurity/armis-cli/internal/supplychain/shell.go:507:			EvalCommand				100.0%
github.com/ArmisSecurity/armis-cli/internal/supplychain/shell.go:511:			HasInjection				75.0%
github.com/ArmisSecurity/armis-cli/internal/supplychain/shell.go:535:			WrappedPMs				95.7%
github.com/ArmisSecurity/armis-cli/internal/supplychain/shell.go:581:			HasCurrentInjection			75.0%
github.com/ArmisSecurity/armis-cli/internal/supplychain/shell.go:590:			fileExists				100.0%
github.com/ArmisSecurity/armis-cli/internal/supplychain/shell.go:606:			IsPipVariant				100.0%
github.com/ArmisSecurity/armis-cli/internal/supplychain/shell.go:620:			CanonicalPipVariant			0.0%
github.com/ArmisSecurity/armis-cli/internal/supplychain/shell.go:658:			scanPathExecutables			84.6%
github.com/ArmisSecurity/armis-cli/internal/supplychain/shell.go:744:			DetectPipVariants			100.0%
github.com/ArmisSecurity/armis-cli/internal/supplychain/shell.go:765:			DetectInstalledPMs			0.0%
github.com/ArmisSecurity/armis-cli/internal/supplychain/shell.go:800:			IsOnPath				100.0%
github.com/ArmisSecurity/armis-cli/internal/supplychain/supplychain.go:37:		ParseTransitivePolicy			100.0%
github.com/ArmisSecurity/armis-cli/internal/supplychain/supplychain.go:54:		DefaultPolicy				100.0%
github.com/ArmisSecurity/armis-cli/internal/supplychain/supplychain.go:70:		ClassifySeverity			100.0%
github.com/ArmisSecurity/armis-cli/internal/supplychain/supplychain.go:80:		IsExcluded				100.0%
github.com/ArmisSecurity/armis-cli/internal/supplychain/supplychain.go:100:		ParseDuration				100.0%
github.com/ArmisSecurity/armis-cli/internal/supplychain/supplychain.go:144:		parseFiniteNonNegativeFloat		100.0%
github.com/ArmisSecurity/armis-cli/internal/supplychain/supplychain.go:166:		scaleToDuration				100.0%
github.com/ArmisSecurity/armis-cli/internal/supplychain/supplychain.go:174:		ViolationToFinding			100.0%
github.com/ArmisSecurity/armis-cli/internal/supplychain/supplychain.go:188:		formatAge				100.0%
github.com/ArmisSecurity/armis-cli/internal/update/update.go:64:			NewChecker				100.0%
github.com/ArmisSecurity/armis-cli/internal/update/update.go:81:			CheckCached				100.0%
github.com/ArmisSecurity/armis-cli/internal/update/update.go:99:			CheckInBackground			100.0%
github.com/ArmisSecurity/armis-cli/internal/update/update.go:119:			check					85.7%
github.com/ArmisSecurity/armis-cli/internal/update/update.go:162:			fetchLatestVersion			89.5%
github.com/ArmisSecurity/armis-cli/internal/update/update.go:196:			getCacheFilePath			66.7%
github.com/ArmisSecurity/armis-cli/internal/update/update.go:211:			readCache				84.6%
github.com/ArmisSecurity/armis-cli/internal/update/update.go:235:			writeCache				76.9%
github.com/ArmisSecurity/armis-cli/internal/update/update.go:259:			IsNewer					100.0%
github.com/ArmisSecurity/armis-cli/internal/update/update.go:282:			parseVersion				100.0%
github.com/ArmisSecurity/armis-cli/internal/update/update.go:305:			FormatNotification			100.0%
github.com/ArmisSecurity/armis-cli/internal/update/update.go:328:			getUpdateCommand			40.0%
github.com/ArmisSecurity/armis-cli/internal/util/cache.go:21:				GetCacheDir				75.0%
github.com/ArmisSecurity/armis-cli/internal/util/cache.go:41:				GetCacheFilePath			80.0%
github.com/ArmisSecurity/armis-cli/internal/util/format.go:7:				FormatCategory				100.0%
github.com/ArmisSecurity/armis-cli/internal/util/mask.go:109:				MaskSecretInLine			86.4%
github.com/ArmisSecurity/armis-cli/internal/util/mask.go:164:				maskValue				83.3%
github.com/ArmisSecurity/armis-cli/internal/util/mask.go:190:				MaskSecretInLines			100.0%
github.com/ArmisSecurity/armis-cli/internal/util/mask.go:204:				MaskSecretInMultiLineString		100.0%
github.com/ArmisSecurity/armis-cli/internal/util/mask.go:218:				MaskSecretsInStringMap			100.0%
github.com/ArmisSecurity/armis-cli/internal/util/path.go:13:				SanitizePath				90.9%
github.com/ArmisSecurity/armis-cli/internal/util/path.go:53:				SafeJoinPath				87.5%
github.com/ArmisSecurity/armis-cli/test/sample-repo/src/main.go:6:			main					0.0%
total:											(statements)				73.8%

@dmitrysmirnov-armis dmitrysmirnov-armis marked this pull request as ready for review June 30, 2026 15:55
@yiftach-armis yiftach-armis requested a review from Copilot July 1, 2026 08:21

@yiftach-armis yiftach-armis left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds interactive SSO authentication to armis-cli using the OAuth2 Device Authorization Grant, with a shared on-disk session store for reuse across CLI runs and MCP plugins, plus supporting commands to manage and inspect the session.

Changes:

  • Introduces armis-cli auth login/logout/whoami and keeps the previous raw-token behavior as hidden auth token.
  • Implements OAuth2 device-flow client, persisted token store (~/.armis/.sessions), and SSO token refresh + provider wiring.
  • Updates auth resolution to prefer stored SSO sessions and optionally auto-trigger device login via ARMIS_DEFAULT_AUTH_METHOD=SSO.

Reviewed changes

Copilot reviewed 21 out of 21 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
README.md Documents recommended auth methods (CI client-credentials vs interactive SSO) and session storage details.
internal/cmd/scan_repo.go Passes command context into auth provider resolution to support interactive login lifetime.
internal/cmd/scan_image.go Passes command context into auth provider resolution to support interactive login lifetime.
internal/cmd/root.go Adds stored-token precedence, SSO auto-login gating, and improved no-credentials messaging.
internal/cmd/root_test.go Updates auth-provider tests for new context.Context signature.
internal/cmd/auth.go Makes auth a visible command group and moves legacy token-print behavior to hidden auth token.
internal/cmd/auth_whoami.go Adds auth whoami for inspecting current auth method/identity/tenant/expiry.
internal/cmd/auth_test.go Adjusts auth command tests for new error messages and SSO token-store isolation.
internal/cmd/auth_logout.go Adds auth logout with environment scoping and --all.
internal/cmd/auth_login.go Adds auth login command and shared device-flow login helper.
internal/cmd/auth_flow_test.go Adds end-to-end tests for login/whoami/logout and SSO auto-login gating.
internal/cmd/agent_detection_collect.go Updates auth provider call to accept a context.
internal/auth/tokenstore.go Implements cross-process token persistence in ~/.armis/.sessions.
internal/auth/tokenstore_test.go Adds coverage for token store behavior, permissions, env scoping, and corruption handling.
internal/auth/oauth_provider_test.go Adds tests for SSO provider refresh behavior and persisted rotation.
internal/auth/device.go Implements OAuth2 device-flow + refresh-token client with proxy/HTTPS/redirect hardening.
internal/auth/device_test.go Adds unit tests for device-flow request/poll/refresh and claim parsing.
internal/auth/client.go Extracts shared URL scheme constants for HTTPS enforcement logic.
internal/auth/browser.go Adds safe browser opener with scheme validation and test override hook.
internal/auth/browser_test.go Adds tests for browser URL validation and opener invocation.
internal/auth/auth.go Extends AuthProvider to support SSO mode, identity/expiry reporting, and refresh-token rotation persistence.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +77 to +79
browseURL := da.VerificationURIComplete
opened := auth.OpenBrowser(browseURL) == nil
printVerificationInstructions(da, browseURL, opened)
Comment on lines +226 to +243
// write persists the entries to the 0600 file, creating ~/.armis (0700) if needed.
func (s *TokenStore) write(entries []tokenEntry) error {
path, err := s.filePath()
if err != nil {
return err
}
data, err := json.MarshalIndent(entries, "", " ") //nolint:gosec // G117: persisting the token blob to its file IS the purpose of this store
if err != nil {
return fmt.Errorf("failed to marshal tokens: %w", err)
}
if err := os.MkdirAll(filepath.Dir(path), 0o700); err != nil {
return fmt.Errorf("failed to create token directory: %w", err)
}
if err := os.WriteFile(path, data, 0o600); err != nil { //nolint:gosec // path derived from os.UserHomeDir + hardcoded segments
return fmt.Errorf("failed to write token file: %w", err)
}
return nil
}
Comment thread internal/cmd/root.go
Comment on lines +511 to +518
func augmentNoCredentialsError(err error) error {
if err == nil || !strings.Contains(err.Error(), "authentication required") {
return err
}
return fmt.Errorf("not authenticated — use one of the following options:\n" +
" - run 'armis-cli auth login' to sign in with your company IdP\n" +
" - or set ARMIS_CLIENT_ID / ARMIS_CLIENT_SECRET (or --client-id / --client-secret) for JWT auth\n" +
" - or set ARMIS_API_TOKEN (or --token) for legacy auth")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants